/*
 * TCSS 422 Process Control Simulator
 * Group Money: Al McKenzie, Michael Pitts, Taylor Zielske
 */

/**
 * An Abstract Shared Process that uses shared data and can do some kind of work on
 * the shared data.
 * @author Michael Pitts
 * @version Dec 8, 2011
 */
public abstract class AbstractSharedProcess extends AbstractProcess {
	
	/** The chance that a process will need to do work on the Shared data. */
	/*protected*/ static final double WORK_PROB = 0.2;
	
	/** Number of iterations to spend working on the Shared data. */
	/*protected*/ static final int WORK_TOTAL = 6;
	
	/** The Shared data used by this Process. */
	/*protected*/ final Shared my_shared;
	
	/** True if currently working on shared data, false otherwise. */
	private boolean my_working;
	
	/** How long this process has left to work on the Shared data. */
	private int my_work_time;
	
	/**
	 * Creates a new Process that can work on the shared data.
	 * @param the_name is the name of the new Process.
	 * @param the_limit is the number of iterations for this process.  If negative,
	 * the process will run forever.
	 * @param the_shared is the shared data.
	 */
	public AbstractSharedProcess(final String the_name, final int the_limit, 
			final Shared the_shared) {
		super(the_name, the_limit);
		my_working = false;
		my_shared = the_shared;
	}
	
	@Override public SysCall run() {
		SysCall call = super.run();
		if (my_working) {// we may need to work
			call = getWork();
		} else if (call != SysCall.DONE) {
			call = getWorkDone();
		} else { // make sure to release semaphore if done.
			my_shared.semaphore_up(this);
		}
		return call;
	}

	/**
	 * Decides if any work should be started or done on the Shared data, or if the
	 * process is done.
	 * @return the results of any work on the Shared data, or normal work.
	 */
	private SysCall getWorkDone() {
		final SysCall call;
		if (Math.random() < WORK_PROB) { // decided to start work process
			call = SysCall.SEM_DOWN;
			my_working = true;
			my_work_time = WORK_TOTAL;
			my_shared.semaphore_down(this);
		} else { // do normal stuff
			Main.out().println(toString() + " cranking the CPU.");
			call = SysCall.NOTHING;
		}
		return call;
	}

	/**
	 * Does one iteration of work on the Shared data.
	 * @return the result of the work.
	 * 
	 */
	private SysCall getWork() {
		SysCall call;
		if (checkData() && my_work_time > 0) { // Do work
			my_work_time--;
			call = work();
		} else { // Done working or Data not ready
			if (!checkData()) { // Shared data not ready.
				Main.out().println("Shared memory not ready for processing.");
				my_shared.semaphore_up(this);
				my_shared.signal_wait(this);
				call = SysCall.NOTHING;
			} else {
				done();
				call = SysCall.SEM_UP;
				my_working = false;
				my_shared.semaphore_up(this);
			}
		}
		return call;
	}
	
	/** Called when the Process is done working for now. */
	/*protected*/ abstract void done();
	
	/** Does work on the shared data.
	 * @return the result of the work. */
	/*protected*/ abstract SysCall work();

	 /** Checks if the shared data is ready to be worked.
	 * @return true if the data is ready to be worked, false otherwise. */
	/*protected*/ abstract boolean checkData();

}
